package com.lucky.common.util;

import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import javax.annotation.PostConstruct;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ResourceUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.time.LocalDate;
import java.util.Map;

/**
 * 给PDF 加水印功能（文字水印和图片水印）
 * 
 * @author yangxp
 */
public class PDFUtil {
	static final Logger logger = LoggerFactory.getLogger(PDFUtil.class);

	static final TemplateEngine templateEngine;
	static final String fontPath;

	static {
        try {
			String simsun = "classpath:static/fonts/simsun.ttf";

			templateEngine = SpringUtil.getBean(TemplateEngine.class);
            fontPath = ResourceUtils.getFile(simsun).getAbsolutePath();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

	/**
	 * 使用 Thymeleaf 渲染 HTML
	 * 页面要用font-family: SimSun;定义字体
	 *
	 * @return 返回渲染后的html代码
	 */
	public static String toHtml(String url) throws IOException {

		return IOUtils.toString(ResourceUtils.getURL(url).openStream());

		//将数据填充到模板里，开始处理模板
	}
	/**
	 * 使用 Thymeleaf 渲染 HTML
	 * 页面要用font-family: SimSun;定义字体
	 *
	 * @param template HTML模板路径
	 * @param params   渲染的参数
	 * @return 返回渲染后的html代码
	 */
	public static String toHtml(String template, Map<String, Object> params) {
		Context context = new Context();
		if (!params.isEmpty()) {
			context.setVariables(params);
		}
		//将数据填充到模板里，开始处理模板
		return templateEngine.process(template, context);
	}


	/**
	 * 根据html生成pdf的base64格式
	 *
	 * @param html
	 * @return
	 */
	public static byte[] htmlToBase64(String html) throws IOException, DocumentException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();//构建字节输出流
		ITextRenderer renderer = new ITextRenderer();
		ITextFontResolver fontResolver = renderer.getFontResolver();
		// html中设置的字体样式需要参考此处debug后fontResolver的fontFamily的数据的key
		//指定文件字体添加到PDF库，指定字体不作为内部字体，而是外部字体被加载

		fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

//        fontResolver.addFont(SimSun, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // SimSun
//        fontResolver.addFont("font/微软雅黑.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // Microsoft YaHei
//        fontResolver.addFont("font/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // SimHei
//        fontResolver.addFont("font/simkai.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // KaiTi
		renderer.setDocumentFromString(html);
		renderer.layout();
		renderer.createPDF(baos);

//        return Base64Util.encode(baos.toByteArray());
		return baos.toByteArray();
	}


	/**
	 * 将HTML内容转换为PDF并写入响应
	 * @throws IOException IO异常
	 * @throws DocumentException 文档异常
	 */
	public static ResponseEntity<byte[]> downloadPDF(byte[] fileByte, String filename) {
		// 输出PDF到响应输出流
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_PDF);
		headers.setContentDisposition(ContentDisposition.attachment().filename(filename).build());

		return ResponseEntity.ok().headers(headers).body(fileByte);
	}



	/**
	 * 将HTML内容转换为PDF并写入响应
	 * @throws IOException IO异常
	 * @throws DocumentException 文档异常
	 */
	public static String toFile(String htmlContent, String filePath) throws IOException, DocumentException {

		// 创建ITextRenderer实例
		ITextRenderer renderer = new ITextRenderer();

		// 设置字体路径，使用 classpath 加载字体
		ITextFontResolver fontResolver = renderer.getFontResolver();
		fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

		renderer.setDocumentFromString(htmlContent);
		renderer.layout();

		// 输出PDF到响应输出流
		try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
			renderer.createPDF(outputStream);
			return filePath;
		} catch (Exception e){
			logger.error(e.getMessage(),e);
		}
		return null;
	}

	/**
	 * 将HTML内容转换为PDF并写入响应
	 * @throws IOException IO异常
	 * @throws DocumentException 文档异常
	 */
	public static byte[] toPdf(String template, Map<String, Object> params) throws IOException, DocumentException {

		String htmlContent = toHtml(template, params);
		return htmlToBase64(htmlContent);
	}

	/**
	 * CSS样式在设置表格和控制分页中的应用。
	 * @param args
	 * @throws DocumentException
	 * @throws IOException
	 */
	/*table {
	 *//*分页时表格换行, 可不用, 使用表格行换行即可*//*
	 *//*page-break-before: always;*//*
        border-collapse: collapse;
    }
    table td,
    table th {
        border: 1px black solid;
    }
    tr {
        *//*分页时表格行换行*//*
        page-break-inside: avoid !important;
    }
*/
	public static void main(String[] args) throws DocumentException, IOException {
		String html = "";

		byte[] base64O = buildPdf(html
				, true, "水印测试123"
				, true, "页眉测试123"
				, true, ResourceUtils.getURL("classpath:static/img/logo-0acbf217.png").toString(), 141, 30, 20, -30
				, true, ResourceUtils.getURL("classpath:static/img/waterLogo_b.png").toString()

		);

		String pdfNumberF = toFile(base64O, "D:\\IDEA\\__SpringBoot\\spring20_pdf\\src\\main\\resources");
		System.out.println(pdfNumberF);

	}


	/**
	 * 根据pdf的base64格式和路径生成pdf文件
	 *
	 * @param base64 pdf的base64格式
	 * @param path   生成pdf的路径
	 * @return
	 */
	public static String toFile(byte[] base64, String path) {

		String fileAdd = AppUtil.fDate(LocalDate.now());
		//先判断文件是否存在
		path = path + "/" + fileAdd;
		String fileName = path + "/" + System.currentTimeMillis() + ".pdf";//新的文件名

//        byte[] bytes = Base64Util.decode(base64);

		try(
				ByteArrayInputStream bais = new ByteArrayInputStream(base64);
				BufferedInputStream bin = new BufferedInputStream(bais);
				// 创建到指定文件的输出流
				FileOutputStream fout = new FileOutputStream(fileName);
				// 为文件输出流对接缓冲输出流对象
				BufferedOutputStream bout = new BufferedOutputStream(fout);) {

			//获取文件夹路径
			File file = new File(path);
			//如果文件夹不存在则创建
			if (!file.exists() && !file.isDirectory()) {
				file.mkdirs();
			}

			byte[] buffers = new byte[1024];
			int len = bin.read(buffers);
			while (len != -1) {
				bout.write(buffers, 0, len);
				len = bin.read(buffers);
			}
			// 刷新此输出流并强制写出所有缓冲的输出字节，必须这行代码，否则有可能有问题
			bout.flush();
			//返回存储的路径
			return fileName;
		} catch (IOException e) {
			logger.error(e.getMessage());
		}
		return "";
	}



	/**
	 * 添加水印
	 *
	 * @param stamper       PdfStamper对象
	 * @param reader        PdfReader对象
	 * @param waterMarkName 水印文字
	 * @throws DocumentException
	 * @throws IOException
	 */
	private static void addWatermark(PdfStamper stamper, PdfReader reader, String waterMarkName) throws DocumentException, IOException {
		BaseFont baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
		Rectangle pageRect;
		PdfGState gs = new PdfGState();
		gs.setFillOpacity(0.1f); // 填充的透明度
		gs.setStrokeOpacity(0.1f); // 描边的透明度

		JLabel label = new JLabel();
		FontMetrics metrics;
		int textH = 0;
		int textW = 0;
		label.setText(waterMarkName);
		metrics = label.getFontMetrics(label.getFont());
		textH = metrics.getHeight();
		textW = metrics.stringWidth(label.getText());

		PdfContentByte under;
		for (int i = 1; i <= reader.getNumberOfPages(); i++) {
			pageRect = reader.getPageSizeWithRotation(i);
			under = stamper.getOverContent(i);
			under.saveState();
			under.setGState(gs);
			under.beginText();
			under.setFontAndSize(baseFont, 20);
			under.setColorFill(BaseColor.BLACK);//水印颜色

			// 水印文字成30度角倾斜
			for (int height = -5 + textH; height < pageRect.getHeight(); height = height + textH * 4) {
				for (int width = -5 + textW; width < pageRect.getWidth() + textW; width = width + textW * 3) {
					under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30);
				}
			}
			// 添加水印文字
			under.endText();
		}
	}

	/**
	 * 添加页眉 (页脚)
	 *
	 * @param stamper PdfStamper对象
	 * @param reader  PdfReader对象
	 * @throws DocumentException
	 * @throws IOException
	 */
	private static void addPageNumbers(PdfStamper stamper, PdfReader reader, String headerText) throws DocumentException, IOException {
		int total = reader.getNumberOfPages();
		for (int i = 1; i <= total; i++) {
			Rectangle pageRect = reader.getPageSizeWithRotation(i);
			PdfContentByte content = stamper.getOverContent(i);
			BaseFont baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			content.setFontAndSize(baseFont, 20);

			ColumnText.showTextAligned(content, Element.ALIGN_CENTER, new Phrase(headerText, new Font(baseFont, 10)),
					pageRect.getWidth() / 2, pageRect.getTop() - 20, 0);

			ColumnText.showTextAligned(content, Element.ALIGN_CENTER, new Phrase("页码 " + i + " / " + total, new Font(baseFont, 10)),
					(pageRect.getLeft() + pageRect.getRight()) / 2, pageRect.getBottom() + 10, 0);


		}
	}

	/**
	 * 添加图片logo
	 *
	 * @param stamper   PdfStamper对象
	 * @param reader    PdfReader对象
	 * @param logoPath  图片路径
	 * @param newWidth  图片大小宽度如 141
	 * @param newHeight 图片大小高度如30
	 * @param absoluteX 图片相对位置 如20
	 * @param absoluteY 图片相对位置 如-30
	 * @throws DocumentException
	 * @throws IOException
	 */
	private static void addLogoImg(PdfStamper stamper, PdfReader reader, String logoPath, float newWidth, float newHeight, float absoluteX, float absoluteY) throws DocumentException, IOException {
		for (int i = 1; i <= reader.getNumberOfPages(); i++) {
			Rectangle pageRect = reader.getPageSizeWithRotation(i);
			PdfContentByte content = stamper.getOverContent(i);
			BaseFont baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
			content.setFontAndSize(baseFont, 20);

			// 添加Logo图片到页眉
			Image logoImage = Image.getInstance(logoPath);
//            logoImage.scaleAbsolute(141, 30);
			logoImage.scaleAbsolute(newWidth, newHeight);
			logoImage.setAbsolutePosition(pageRect.getLeft() + absoluteX, pageRect.getTop() + absoluteY);
			content.addImage(logoImage);

		}
	}

	/**
	 * 添加水印图片
	 *
	 * @param stamper       PdfStamper对象
	 * @param reader        PdfReader对象
	 * @param watermarkPath 水印图片路径
	 * @throws DocumentException
	 * @throws IOException
	 */
	private static void addWatermarkImage(PdfStamper stamper, PdfReader reader, String watermarkPath) throws DocumentException, IOException {
		Image watermarkImage = Image.getInstance(watermarkPath);
		watermarkImage.scaleToFit(400, 400); // 调整水印图片大小

		/**
		 * .scaleAbsolute    .scaleToFit 这两个方法 什么区别
		 * scaleAbsolute和scaleToFit是iText库中用于缩放图像的两个方法。
		 *
		 * scaleAbsolute(float width, float height):
		 * 该方法用于将图像缩放到指定的绝对宽度和高度。
		 * 参数width和height分别指定了缩放后的宽度和高度。
		 * 图像将按照指定的宽度和高度进行缩放，可能会导致图像的比例失调。
		 *
		 * scaleToFit(float width, float height):
		 * 该方法用于将图像缩放以适应指定的矩形框的大小。
		 * 参数width和height指定了矩形框的宽度和高度。
		 * 图像将按照比例缩放，以适应指定的矩形框，同时保持其宽高比。
		 * 简而言之，scaleAbsolute方法按照指定的宽度和高度进行缩放，而scaleToFit方法会按照比例缩放以适应指定的矩形框大小。选择使用哪个方法取决于你的需求和图像的要求。
		 */

		for (int i = 1; i <= reader.getNumberOfPages(); i++) {
			Rectangle pageRect = reader.getPageSizeWithRotation(i);
			PdfContentByte content = stamper.getOverContent(i);

			// 创建PdfGState对象并设置透明度
			PdfGState gState = new PdfGState();
			gState.setFillOpacity(0.1f); // 水印图片透明度 (0.0f - 1.0f，0.0f 表示完全透明，1.0f 表示完全不透明)

			content.saveState();
			content.setGState(gState);

			// 添加水印图片到页面
			float x = (pageRect.getLeft() + pageRect.getRight() - watermarkImage.getScaledWidth()) / 2;
			float y = (pageRect.getBottom() + pageRect.getTop() - watermarkImage.getScaledHeight()) / 2;
			content.addImage(watermarkImage, watermarkImage.getScaledWidth(), 0, 0, watermarkImage.getScaledHeight(), x, y);

			content.restoreState();
		}
	}

 /*<style>
    @page {
        size: A4; *//* 设置页面大小为A4 *//*
        margin: 10mm; *//* 设置页面边距为30毫米 *//*
    }
    html,body{
        font-family: simsun,SimSun,simsunb,msyh;
    }
    </style>*/

	/**
	 * html里一定要带上字体，纸张大小也可以。甚至页眉页脚水印都可以通过css来设置.
	 * 尤其openhtmltopdf，代码没有直接支持水印什么的，就是让你在HTML里用样式处理。
	 */
    /*public void buildPdf(String htmlContent) throws Exception {
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.useFont(ResourceUtils.getFile("classpath:font/SimSun.ttc"), "simsun");
        builder.useFont(ResourceUtils.getFile("classpath:font/simsun.ttc"), "simsunb");
        builder.useFont(ResourceUtils.getFile("classpath:font/msyh.ttc"), "msyh");

        builder.useFastMode();
        builder.withHtmlContent(htmlContent, "").toString();
        String outputPath = "D:\\svndocs\\55555.pdf";
        try (OutputStream os = new FileOutputStream(outputPath)) {
            builder.toStream(os);
            builder.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/




	/**
	 * 合并添加水印 页眉和页脚 水印的方法 (可根据需求自行改动封装)
	 *
	 * @param html             html内容
	 * @param isAddWatermark   是否添加水印
	 * @param waterMarkName    水印文字
	 * @param isAddPageNumbers 是否添加页眉
	 * @param headerText       页眉名称
	 * @param isAddLogoImg     是否添加Logo
	 * @param logoPath         logo图片路径
	 * @param newWidth         图片大小宽度如 141
	 * @param newHeight        图片大小高度如30
	 * @param absoluteX        图片相对位置 如20
	 * @param absoluteY        图片相对位置 如-30
	 * @return
	 */
	public static byte[] buildPdf(String html
			, boolean isAddWatermark, String waterMarkName
			, boolean isAddPageNumbers, String headerText
			, boolean isAddLogoImg, String logoPath, float newWidth, float newHeight, float absoluteX, float absoluteY
			, boolean isAddWatermarkLogo, String watermarkLogoPath
	) {
		try {
			byte[] pdfBase64 = PDFUtil.htmlToBase64(html);
			/**
			 * PdfReader是iText库中的PDF解析器，用于读取和解析PDF文件。通过PdfReader对象，可以获取PDF文件的各种属性和内容，
			 * 如总页数、页面尺寸、文本内容等。它提供了方法来打开和关闭PDF文件，并可以从文件路径、InputStream或字节数组等多种方式加载PDF文件。
			 *
			 * PdfStamper是iText库中的PDF编辑器，用于修改和编辑PDF文件。通过PdfStamper对象，
			 * 可以在PDF文件中添加文本、图片、表单域等元素，修改页面内容、添加注释、加密文档等操作。它基于已解析的PdfReader对象，可以将修改后的内容写入新的PDF文件或覆盖原始文件。
			 */
			PdfReader reader = new PdfReader(pdfBase64);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			PdfStamper stamper = new PdfStamper(reader, bos);
			int total = reader.getNumberOfPages();

			if (isAddLogoImg) {
				addLogoImg(stamper, reader, logoPath, newWidth, newHeight, absoluteX, absoluteY);
			}


			if (isAddPageNumbers) {
				addPageNumbers(stamper, reader, headerText);
			}

			// 添加水印放到最后执行 否则会将页眉页脚进行透明度设置
			if (isAddWatermark) {
				addWatermark(stamper, reader, waterMarkName);
			}

			if (isAddWatermarkLogo) {
				addWatermarkImage(stamper, reader, watermarkLogoPath);
			}


			stamper.flush();
			stamper.close();
			reader.close();
			return bos.toByteArray();
		} catch (IOException | DocumentException e) {
			logger.error(e.getMessage());
		}
		return null;
	}

	/**
	 * 基础pdf生成器
	 *
	 * @param pdfBase64
	 * @return
	 */
	public static byte[] buildPdf(byte[] pdfBase64) {
		try {
//            byte[] bytes = new BASE64Decoder().decodeBuffer(pdfBase64);
			/**
			 * PdfReader是iText库中的PDF解析器，用于读取和解析PDF文件。通过PdfReader对象，可以获取PDF文件的各种属性和内容，
			 * 如总页数、页面尺寸、文本内容等。它提供了方法来打开和关闭PDF文件，并可以从文件路径、InputStream或字节数组等多种方式加载PDF文件。
			 *
			 * PdfStamper是iText库中的PDF编辑器，用于修改和编辑PDF文件。通过PdfStamper对象，
			 * 可以在PDF文件中添加文本、图片、表单域等元素，修改页面内容、添加注释、加密文档等操作。它基于已解析的PdfReader对象，可以将修改后的内容写入新的PDF文件或覆盖原始文件。
			 */
			PdfReader reader = new PdfReader(pdfBase64);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			PdfStamper stamper = new PdfStamper(reader, bos);

			stamper.close();
			reader.close();
			return Base64Util.encode(bos.toByteArray());
		} catch (IOException | DocumentException e) {
			e.printStackTrace();
		}
		return null;
	}

/*

	public static void toPdf2File(String htmlContent, String filePath) throws Exception {
		PdfRendererBuilder builder = new PdfRendererBuilder();
		builder.useFont(ResourceUtils.getFile(SimSun), "simsun");
//		builder.useFont(ResourceUtils.getFile("classpath:font/simsunb.ttf"), "simsunb");

		builder.useFastMode();
		builder.withHtmlContent(htmlContent, "");
//		builder.withW3cDocument(new W3CDom().fromJsoup(Jsoup.parse(htmlContent)), "");
		try (FileOutputStream fos = new FileOutputStream(filePath)) {
			builder.toStream(fos);
			builder.run();
		}catch (Exception e) {
			logger.error(e.getMessage());
		}
	}

	public static void toPDFFromURL(String url, String filePath) throws Exception {
		PdfRendererBuilder builder = new PdfRendererBuilder();
		builder.useFont(ResourceUtils.getFile(SimSun), "simsun");
		builder.useFastMode();
		builder.withUri(url);
		try (FileOutputStream fos = new FileOutputStream(filePath)) {
			builder.toStream(fos);
			builder.run();
		}catch (Exception e) {
			logger.error(e.getMessage());
		}
	}*/
		
}
